﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using AjaxControlToolkit;
using System.Net;
using System.Text.RegularExpressions;

namespace HTTPDebuggerTools.com
{
    public partial class WebOptimizer : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            NoBotState state;
            if (!NoBot1.IsValid(out state))
                Response.Redirect("http://www.httpdebugger.com/");
        }

        protected void OptimizeBtn_Click(object sender, EventArgs e)
        {
            try
            {
                String result = null;

                switch (TypeList.SelectedValue)
                {
                    case "HTML":
                        OptimizeHTML(SourceBox.Text, out result);
                        break;

                    case "CSS":
                        OptimizeCSS(SourceBox.Text, out result);
                        break;

                    case "JavaScript":
                        OptimizeJavaScript(SourceBox.Text, out result);
                        break;
                }

                //now we can process the result
                if (!String.IsNullOrEmpty(result))
                    ProcessResult(result);
            }
            catch (Exception exp)
            {
                ResultLiteral.Text = "<span class='statuserr'>" + exp.Message + "</span>";
            }
        }

        void ProcessResult(string result)
        {
            result = result.Replace("<", "&lt;");
            //result = result.Replace(">", "&rt;");

            ResultLiteral.Text = "<h2>Optimized Code</h2><pre class='brush: html; wrap-lines: true;'>\r\n" + result + "\r\n</pre>";
        }

        public bool OptimizeHTML(string html, out string result)
        {
            result = html;

            const string sq = "[\"']"; //quotes
            const string nsq = "^[\"']";
            const string sComm = @"(?<comm><!--[\s\S]*?-->)";
            const string sScript = @"(?<script><script[^<>]*>(?<jsBody>([\s\S]|(" + sq + @"{1}" + nsq + @"*?</script[^<>]*>.*?" + sq + @"{1}))*?)</script[^<>]*>(?![^\s<]))";
            
            //@"(?<script><\script[^>]*javascript[^>]*>(?<jsBody>([\s\S]*|(" + sq + @"*?</script\s*>" + sq  + @"*?))*?)</script\s*>)";
            const string sStyle = @"(?<style><\s*style[^>]*>(?<styleBody>[\s\S]*?)</\s*style\s*>)";
            const string sTag = @"(?<tag><[^>]*>)";
            const string sOtherTrash = @"[^<>]*";

            string[] unfmtTags = {	"<html", "<!doctype", "<head", "<meta", "<title", "<link", "<table",
									"</div" };
            string sUnformattedTags = regOnOf(unfmtTags);

            const string sRe = "(" + sScript + "|" + sStyle + "|" + sComm + ")" + "|" + sTag + "|" + sOtherTrash;

            Regex re = new Regex(sRe, RegexOptions.IgnoreCase);

            String nr = "";

            bool isUnformat = false;
            bool needsSpace = false;

            foreach (Match m in re.Matches(result))
            {
                String sNew = "";

                if (m.Groups["script"].Success)
                {
                    sNew = m.Value;
                    int i = m.Groups["jsBody"].Index - m.Groups["script"].Index;
                    int l = m.Groups["jsBody"].Length;
                    sNew = sNew.Substring(0, i) + optimizeJavaScript(m.Groups["jsBody"].Value) + sNew.Substring(i + l);
                }
                else if (m.Groups["style"].Success)
                {
                    sNew = m.Value;
                    int i = m.Groups["styleBody"].Index - m.Groups["style"].Index;
                    int l = m.Groups["styleBody"].Length;
                    sNew = sNew.Substring(0, i) + optimizeCSS(m.Groups["styleBody"].Value) + sNew.Substring(i + l);
                }
                else if (m.Groups["comm"].Success)
                {
                    sNew = "";
                }
                else if (m.Groups["tag"].Success)
                {
                    sNew = removeSpace(m.Value, "=");
                    sNew = removeSpace(sNew, ">");
                    sNew = removeSpace(sNew, "</");
                    sNew = Regex.Replace(sNew, @"\s+", " ");
                    sNew = Regex.Replace(sNew, @"(?<=style\s*=" + "\"" + @").*(?=" + "\"" + ")", (mtch) => optimizeCSS(mtch.Value), RegexOptions.IgnoreCase);
                    if (Regex.Match(sNew, @"^<(pre|listing|textarea)", RegexOptions.IgnoreCase).Success)
                        isUnformat = true;
                    else if (Regex.Match(sNew, @"^</(pre|listing|textarea)", RegexOptions.IgnoreCase).Success)
                        isUnformat = false;
                    else if (Regex.Match(sNew, sUnformattedTags, RegexOptions.IgnoreCase).Success)
                        needsSpace = false;
                    else
                        needsSpace = true;
                }
                else
                {
                    sNew = m.Value;
                    if (!isUnformat)
                    {
                        if (!needsSpace) sNew = Regex.Replace(sNew, @"^[\s]+$", "");
                        sNew = Regex.Replace(sNew, @"[\s]", " ");
                        sNew = Regex.Replace(sNew, @" +", " ");
                    }
                }

                nr += sNew;
            }

            result = nr;
            return true;
        }

        string regOnOf(string[] strs)
        {
            string sRet = "(" + strs[0];

            for (int i = 1; i < strs.Length; i++)
            {
                sRet += "|" + strs[i];
            }

            return sRet + ")";
        }

        public bool OptimizeCSS(string css, out string result)
        {
            result = optimizeCSS(css);
            return (result == null);
        }

        string optimizeCSS(string css)
        {
            String[] specs = { "{", "}", "<", "!", ">", "-", ";", ":" };

            String sOutput = css;

            sOutput = Regex.Replace(sOutput, @"^\s+", "");
            sOutput = Regex.Replace(sOutput, @"\s+$", "");

            foreach (String spec in specs)
            {
                sOutput = Regex.Replace(sOutput, @"\s+" + spec, spec);
                sOutput = Regex.Replace(sOutput, spec + @"\s+", spec);
            }

            String[] slashSpecs = { ".", "(", ")" };

            foreach (String spec in slashSpecs)
            {
                sOutput = Regex.Replace(sOutput, @"\s+\" + spec, spec);
                sOutput = Regex.Replace(sOutput, @"\" + spec + @"\s+", spec);
            }

            return sOutput;
        }

        string optimizeJavaScript(string js)
        {
            String sOutput = js;

            string sq1 = "\\\"", sq2 = "\\\'", ss = "\\\\";
            string requoted = sq1 + @"(?:[^" + sq1 + @"\012]*" + ss + @"[" + sq1 + @"\012])*[^" + sq1 + @"\012]*?" + sq1 +
                            "|" + sq2 + @"(?:[^" + sq2 + @"\012]*" + ss + @"[" + sq2 + @"\012])*[^" + sq2 + @"\012]*?" + sq2;

            requoted = "(?<requoted>" + requoted + ")";

            string recomment1 = @"(//[^\012]*(?=\-\->))|//[^\012]*";
            string recomment2 = @"/\*[\s\S]*?\*/";

            string comms = requoted + "|" + recomment1 + "|" + recomment2;

            sOutput = Regex.Replace(sOutput, comms, (m) => (!m.Groups["requoted"].Success) ? "" : m.Value);

            String[] specs = { "{", "}", "<", "!", ">", "-", "=", ";", ",", "&", ";", "/", ":" };

            sOutput = Regex.Replace(sOutput, @"^\s+", "");
            sOutput = Regex.Replace(sOutput, @"\s+$", "");

            foreach (String spec in specs)
            {
                sOutput = Regex.Replace(sOutput, String.Format(@"({0})|\s+{1}", requoted, spec), (m) => (!m.Groups["requoted"].Success) ? spec : m.Value);
                sOutput = Regex.Replace(sOutput, String.Format(@"({0})|{1}\s+", requoted, spec), (m) => (!m.Groups["requoted"].Success) ? spec : m.Value);
            }

            String[] slashSpecs = { ".", "(", ")", "[", "]", "?", "|", @"\", "+" };

            foreach (String spec in slashSpecs)
            {
                sOutput = Regex.Replace(sOutput, String.Format(@"({0})|\{1}\s+", requoted, spec), (m) => (!m.Groups["requoted"].Success) ? spec : m.Value);
                sOutput = Regex.Replace(sOutput, String.Format(@"({0})|\s+\{1}", requoted, spec), (m) => (!m.Groups["requoted"].Success) ? spec : m.Value);
            }

            //sOutput = Regex.Replace(sOutput, @"^\s*", "", RegexOptions.Multiline);
            //sOutput = Regex.Replace(sOutput, @"\s*$", "", RegexOptions.Multiline);

            return sOutput;
        }

        string removeSpace(string sOutput, string spec)
        {
            sOutput = Regex.Replace(sOutput, @"\s+\" + spec, spec);
            sOutput = Regex.Replace(sOutput, @"\" + spec + @"\s+", spec);
            return sOutput;
        }

        public bool OptimizeJavaScript(string javaScript, out string result)
        {
            result = optimizeJavaScript(javaScript);
            return (result == null);
        }
    }
}
